package com.surfilter.msmp.common.util.httpclient;


import org.apache.http.HttpHost;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.protocol.HttpContext;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;

/**
 * 自定义 socket 连接 
 * 
 * @author well
 * @date 2013-10-14
 * 
 * @since 4.3
 */
public class PlainSocksProxyConnectionSocketFactory implements ConnectionSocketFactory {
	
	public static final PlainSocksProxyConnectionSocketFactory INSTANCE = new PlainSocksProxyConnectionSocketFactory();
	
	public static PlainSocksProxyConnectionSocketFactory getSocketFactory() {
        return INSTANCE;
    }
	
    public PlainSocksProxyConnectionSocketFactory() {
        super();
    }
    
	@Override
	public Socket createSocket(final HttpContext context) throws IOException {
		InetSocketAddress socksaddr = (InetSocketAddress) context.getAttribute(HttpClientConstant.SOCKS_ADDRESS);

		// socksaddr is not set, use default socket
		if (socksaddr == null){
			return new Socket();
		}
		java.net.Proxy proxy = new java.net.Proxy(java.net.Proxy.Type.SOCKS, socksaddr);
		return new Socket(proxy);
	}

	@Override
	public Socket connectSocket(final int connectTimeout, final Socket socket, final HttpHost host, final InetSocketAddress remoteAddress,
								final InetSocketAddress localAddress, final HttpContext context) throws IOException, ConnectTimeoutException {
		final Socket sock = socket != null ? socket : createSocket(context);

		// reset sock param
		sock.setTcpNoDelay(true);
		sock.setSoLinger(true, HttpClientConstant.SOLINGER_TIMEOUT);
		sock.setReuseAddress(true);
		sock.setSoTimeout(HttpClientConstant.SO_TIMEOUT);

		if (localAddress != null) {
			sock.bind(localAddress);
		}

		try {
			sock.connect(remoteAddress, connectTimeout);
		} catch (SocketTimeoutException ex) {
			try {
				sock.close();
			} catch (final IOException ignore) {
			}
			throw new ConnectTimeoutException("Connect to " + remoteAddress.getHostName() + "/" + remoteAddress.getAddress() + " timed out");
		} catch (SocketException ex) {
			try {
				sock.close();
			} catch (final IOException ignore) {
			}
			throw ex;
		} catch (final IOException ex) {
			try {
				sock.close();
			} catch (final IOException ignore) {
			}
			throw ex;
		} catch (Exception e) {
			try {
				sock.close();
			} catch (final IOException ignore) {
			}
		}
		return sock;
	}
	
}
